﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Abp.Application.Services.Dto;
using Abp.Runtime.Caching;
using DevM.Development.ReleaseRecords.Dto;
using DevM.Sessions;
using Microsoft.AspNetCore.Http;
using Abp.Linq.Extensions;
using System.Linq;
using DevM.Development.Projects;
using DevM.Development.Projects.Dto;
using DevM.Development.DataMigration;
using DevM.Development.Resources.Dto;
using DevM.Development.Resources;

namespace DevM.Development.ReleaseRecords
{
    public class ReleaseRecordAppService : DevMAppServiceBase, IReleaseRecordAppService
    {
        private readonly ISessionAppService sessionAppService;
        private readonly IHttpContextAccessor httpContextAccessor;
        private readonly ReleaseRecordManager releaseRecordManager;
        private readonly ProjectManager projectManager;
        private readonly ICacheManager cacheManager;
        private readonly DataMigrationManager dataMigrationManager;
        private readonly ResourceManager resourceManager;

        public ReleaseRecordAppService(ISessionAppService sessionAppService, IHttpContextAccessor httpContextAccessor, ReleaseRecordManager releaseRecordManager, ProjectManager projectManager, ICacheManager cacheManager, DataMigrationManager dataMigrationManager, ResourceManager resourceManager)
        {
            this.sessionAppService = sessionAppService;
            this.httpContextAccessor = httpContextAccessor;
            this.releaseRecordManager = releaseRecordManager;
            this.projectManager = projectManager;
            this.cacheManager = cacheManager;
            this.dataMigrationManager = dataMigrationManager;
            this.resourceManager = resourceManager;
        }

        public async Task<FeedbackDto> AddFeedback(AddFeedbackInput input)
        {
            var feedback = ObjectMapper.Map<Feedback>(input);
            var currentUserInfomation = await sessionAppService.GetCurrentLoginInformations();
            feedback.CreationTime = DateTime.Now;
            feedback.Status = Feedback.FeedbackStatus.Pending;
            feedback.CreatorUserId = currentUserInfomation.User?.Id ?? 0;
            feedback.TenantId = currentUserInfomation.Tenant?.Id;
            feedback = await releaseRecordManager.AddFeedback(feedback);
            var feedbackResources = input.ResourceIds.Select(r => new RecordResource
            {
                CreationTime = DateTime.Now,
                CreatorUserId = feedback.CreatorUserId,
                TenantId = feedback.TenantId,
                ResourceId = r,
                RecoordId = feedback.Id,
                RecoordType = typeof(Feedback).FullName
            }).ToList();
            await resourceManager.AddRecordResource(feedbackResources);

            return ObjectMapper.Map<FeedbackDto>(feedback);
        }

        public async Task DeleteFeedback(EntityDto<long> feedbackId)
        {
            await releaseRecordManager.DeleteFeedback(feedbackId.Id);
            var feedbackResources = resourceManager.RecordResources.Where(rr => rr.RecoordId == feedbackId.Id && rr.RecoordType == typeof(Feedback).FullName);
            await resourceManager.DeleteRecordResource(typeof(Feedback).FullName, feedbackResources.Select(fr => fr.Id).ToArray());
        }

        public async Task<PageOutput<FeedbackDto>> GetFeedbackList(GetFeedbackListInput input)
        {
            var releaseLog = releaseRecordManager.ReleaseLogs.Where(r => r.ReleaseRecordId == input.ReleaseRecordId && r.ReleaseEnvironmentId == input.EnvironmentId).FirstOrDefault();
            if (releaseLog == null)
                releaseLog = releaseRecordManager.ReleaseLogs.Where(r => r.Id == input.ReleaseLogId).FirstOrDefault();

            var dataset = releaseRecordManager.Feedbacks.Where(f => f.ReleaseLogId == releaseLog.Id).OrderBy(f => f.Status).OrderByDescending(f => f.Id);
            var items = dataset.Skip(input.SkipCount).Take(input.MaxResultCount).ToList();
            var total = dataset.Count();
            var resultItems = ObjectMapper.Map<List<FeedbackDto>>(items);

            return new PageOutput<FeedbackDto>
            {
                Items = resultItems,
                TotalCount = total
            };
        }
        public async Task<FeedbackDto> UpdateFeedback(UpdateFeedbackInput input)
        {
            var feedback = ObjectMapper.Map<Feedback>(input);
            var currentUserInfomation = await sessionAppService.GetCurrentLoginInformations();
            feedback.LastModificationTime = DateTime.Now;
            feedback.LastModifierUserId = currentUserInfomation.User?.Id ?? 0;
            feedback = await releaseRecordManager.UpdateFeedback(feedback);

            var feedbackResources = input.ResourceIds.Select(r => new RecordResource
            {
                CreationTime = DateTime.Now,
                CreatorUserId = feedback.CreatorUserId,
                TenantId = feedback.TenantId,
                ResourceId = r,
                RecoordId = feedback.Id,
                RecoordType = typeof(Feedback).FullName
            }).ToList();
            await resourceManager.AddRecordResource(feedbackResources);

            return ObjectMapper.Map<FeedbackDto>(feedback);
        }

        public async Task<PageOutput<ReleaseLogDto>> GetReleaseLogList(GetReleaseLogInput input)
        {
            var releaseRecords = releaseRecordManager.ReleaseRecords.WhereIf(input.ProjectId > 0, r => r.ProjectId == input.ProjectId).Select(r => r.Id).ToList();
            var dataset = releaseRecordManager.ReleaseLogs.WhereIf(input.ProjectId > 0, r => releaseRecords.Contains(r.ReleaseRecordId)).OrderByDescending(r => r.CreationTime);
            var items = dataset.Skip(input.SkipCount).Take(input.MaxResultCount).ToList();
            var total = dataset.Count();
            var resultItems = ObjectMapper.Map<List<ReleaseLogDto>>(items);
            resultItems.ForEach(async r =>
            {
                r.ReleaseEnvironment = ObjectMapper.Map<EnvironmentDto>(await projectManager.GetEnvironmentById(r.ReleaseEnvironmentId));
                //r.ReleaseRecord = ObjectMapper.Map<ReleaseRecordDto>(releaseRecordManager.ReleaseRecords.Where(rr=>rr.Id==r.ReleaseRecordId).FirstOrDefault());
            });
            return new PageOutput<ReleaseLogDto>
            {
                Items = resultItems,
                TotalCount = total
            };
        }

        public async Task<PageOutput<ReleaseRecordDto>> GetReleaseRecordList(GetReleaseRecordInput input)
        {
            var ddd = await dataMigrationManager.GetConfigsCount(28);
            var currentUserInfomation = await sessionAppService.GetCurrentLoginInformations();
            //如果列表没有当前项则自动插入一条
            if (releaseRecordManager.ReleaseRecords.WhereIf(input.ProjectId > 0, r => r.ProjectId == input.ProjectId && !r.HasReleased).Count() == 0)
            {
                lock ($"GetReleaseRecordList-{input.ProjectId}")
                {
                    if (releaseRecordManager.ReleaseRecords.WhereIf(input.ProjectId > 0, r => r.ProjectId == input.ProjectId && !r.HasReleased).Count() == 0)
                    {
                        releaseRecordManager.AddReleaseRecord(input.ProjectId, currentUserInfomation.User?.Id ?? 0, currentUserInfomation.Tenant?.Id);
                    }
                }
            }

            var dataset = releaseRecordManager.ReleaseRecords.WhereIf(input.ProjectId > 0, r => r.ProjectId == input.ProjectId).OrderByDescending(r => r.CreationTime);
            var items = dataset.Skip(input.SkipCount).Take(input.MaxResultCount).ToList();
            var total = dataset.Count();
            var resultItems = ObjectMapper.Map<List<ReleaseRecordDto>>(items);
            resultItems.ForEach(async r =>
            {
                r.ConfigsCount = await dataMigrationManager.GetConfigsCount(r.Id);
                r.DatabasesCount = await dataMigrationManager.GetDatasCount(r.Id);
                r.DatasCount = await dataMigrationManager.GetDatabasesCount(r.Id);
            });
            resultItems.ForEach(async r =>
            {
                r.CurrentEnvironment = ObjectMapper.Map<EnvironmentDto>(await projectManager.GetEnvironmentById(r.CurrentEnvironmentId));
            });
            return new PageOutput<ReleaseRecordDto>
            {
                Items = resultItems,
                TotalCount = total
            };
        }

        public async Task<ReleaseLogDto> Release(ReleaseInput input)
        {
            var releaseLog = ObjectMapper.Map<ReleaseLog>(input);
            var currentUserInfomation = await sessionAppService.GetCurrentLoginInformations();
            releaseLog.CreationTime = DateTime.Now;
            releaseLog.CreatorUserId = currentUserInfomation.User?.Id ?? 0;
            releaseLog.TenantId = currentUserInfomation.Tenant?.Id;
            releaseLog = await releaseRecordManager.Release(releaseLog);
            if (releaseLog == null) throw new Exception("发布失败");
            return ObjectMapper.Map<ReleaseLogDto>(releaseLog);
        }

        public async Task<ReleaseRecordDto> GetReleaseRecord(EntityDto<long> releaseRecordId)
        {
            var result = ObjectMapper.Map<ReleaseRecordDto>(releaseRecordManager.ReleaseRecords.Where(c => c.Id == releaseRecordId.Id).FirstOrDefault());
            if (result != null)
            {
                result.CurrentEnvironment = ObjectMapper.Map<EnvironmentDto>(await projectManager.GetEnvironmentById(result.CurrentEnvironmentId));
            }
            return result;
        }

        public async Task<ReleaseLogDto> GetReleaseLog(EntityDto<long> releaseLogId)
        {
            var releaseLog = releaseRecordManager.ReleaseLogs.WhereIf(releaseLogId.Id > 0, r => r.Id == releaseLogId.Id).FirstOrDefault();
            var releaseLogDto = ObjectMapper.Map<ReleaseLogDto>(releaseLog);
            if (releaseLogDto != null)
            {
                releaseLogDto.ReleaseEnvironment = ObjectMapper.Map<EnvironmentDto>(await projectManager.GetEnvironmentById(releaseLogDto.ReleaseEnvironmentId));
                releaseLogDto.ReleaseRecord = ObjectMapper.Map<ReleaseRecordDto>(releaseRecordManager.ReleaseRecords.Where(r => r.Id == releaseLogDto.ReleaseRecordId).FirstOrDefault());
            }
            return releaseLogDto;
        }

        public async Task<FeedbackDto> GetFeedback(EntityDto<long> feedbackId)
        {
            var feedback = ObjectMapper.Map<FeedbackDto>(releaseRecordManager.Feedbacks.WhereIf(feedbackId.Id > 0, f => f.Id == feedbackId.Id).FirstOrDefault());

            var feedbackResources = await resourceManager.GetRecordResources(typeof(Feedback).FullName, feedbackId.Id);
            var resourcesDto = feedbackResources.Select(rr => new FeedbackResourceDto { Id = rr.Id, FeedbackId = rr.RecoordId, ResourceId = rr.ResourceId }).ToList();
            resourcesDto.ForEach(async r =>
            {
                r.Resource = ObjectMapper.Map<ResourceDto>((await resourceManager.GetResourceListById(r.ResourceId)).FirstOrDefault());
            });

            feedback.Resources = resourcesDto;
            return feedback;
        }

        public async Task<ReleaseLogDto> GetReleaseLog(EntityDto<long> releaseRecordId, EntityDto<long> environmentId)
        {
            var releaseLog = releaseRecordManager.ReleaseLogs.Where(r => r.ReleaseRecordId == releaseRecordId.Id && r.ReleaseEnvironmentId == environmentId.Id).FirstOrDefault();

            var releaseLogDto = ObjectMapper.Map<ReleaseLogDto>(releaseLog);
            if (releaseLogDto != null)
            {
                releaseLogDto.ReleaseEnvironment = ObjectMapper.Map<EnvironmentDto>(await projectManager.GetEnvironmentById(releaseLogDto.ReleaseEnvironmentId));
                releaseLogDto.ReleaseRecord = ObjectMapper.Map<ReleaseRecordDto>(releaseRecordManager.ReleaseRecords.Where(r => r.Id == releaseLogDto.ReleaseRecordId).FirstOrDefault());
            }
            return releaseLogDto;

        }

        public async Task<FeedbackDto> UpdateFeedbackStatus(UpdateFeedbackStatusInput input)
        {
            var feedback = ObjectMapper.Map<Feedback>(input);
            var _feedback = releaseRecordManager.Feedbacks.Where(f => f.Id == feedback.Id).FirstOrDefault();
            _feedback.Status = feedback.Status;
            return ObjectMapper.Map<FeedbackDto>(await releaseRecordManager.UpdateFeedback(_feedback));
        }

        public async Task<GetFeedbackStaticsOutput> GetFeedbackStatics(GetFeedbackStaticsInput input)
        {
            var releaseLog = releaseRecordManager.ReleaseLogs.Where(r => r.ReleaseRecordId == input.ReleaseRecordId && r.ReleaseEnvironmentId == input.EnvironmentId).FirstOrDefault();
            if (releaseLog == null)
                releaseLog = releaseRecordManager.ReleaseLogs.Where(r => r.Id == input.ReleaseLogId).FirstOrDefault();

            return new GetFeedbackStaticsOutput
            {
                PendingTotal = releaseRecordManager.Feedbacks.Where(f => f.ReleaseLogId == releaseLog.Id && f.Status == Feedback.FeedbackStatus.Pending).Count(),
                ClaimedTotal = releaseRecordManager.Feedbacks.Where(f => f.ReleaseLogId == releaseLog.Id && f.Status == Feedback.FeedbackStatus.Claimed).Count(),
                ProcessingTotal = releaseRecordManager.Feedbacks.Where(f => f.ReleaseLogId == releaseLog.Id && f.Status == Feedback.FeedbackStatus.Processing).Count(),
                ProcessedTotal = releaseRecordManager.Feedbacks.Where(f => f.ReleaseLogId == releaseLog.Id && f.Status == Feedback.FeedbackStatus.Processed).Count()
            };
        }

        public async Task<List<ResourceDto>> GetFeedbackResourcesByFeedbackId(EntityDto<long> feedbackId)
        {
            var feedbackResources = await resourceManager.GetRecordResources(typeof(Feedback).FullName, feedbackId.Id);
            return ObjectMapper.Map<List<ResourceDto>>(await resourceManager.GetResourceListById(feedbackResources.Select(fr => fr.ResourceId).ToArray()));
        }

        public async Task DeleteFeedbackResource(EntityDto<long> feedbackResourceId)
        {
            await resourceManager.DeleteRecordResource(typeof(Feedback).FullName, feedbackResourceId.Id);
        }
    }
}
